using System;
using System.Collections.Generic;
using CI.DAL;
using CI.Interfaces;
using CI.Misc;
using CI.Utils;

namespace CI.Mesopotamia
{
    /// <summary>
    /// Mesoptaima Boot strap, this loads the org in memory from the db, starts the core, then shuts it all
    /// down and saves back to the db
    /// </summary>
    public class ScenarioHostBootstrap
    {
        /// <summary>Data Access Layer</summary>
        public IDAL DAL { private get; set; }

        /// <summary>Hosting Environment</summary>
        public IHostingEnvironment HostingEnvironment { get; private set; }
        private System.Threading.Timer _timer;
        private readonly SimEnv _simEnv = SimEnv.Instance;

        /// <summary>
        /// Start the loading
        /// </summary>
        public void Start(IHostingEnvironmentPhysical hostingEnvironmentPhysical, IScenario scenario, List<Guid> SelectedOrganismsToLoad)
        {

            // Setup the Hosting Environment
            HostingEnvironment = new MSRBHost(hostingEnvironmentPhysical, scenario);

            _simEnv.TraceLogger.LogDebug(string.Format("Scenrio Version {0} vs Hosting Version {1}", scenario.HostingEnvironmentVersionInformation, HostingEnvironment.VersionInformation));

            // make sure the scenario version is the same as hosting else recreated it
            if (scenario.HostingEnvironmentVersionInformation != HostingEnvironment.VersionInformation)
            {
                // delete the old files
                RoboticsConfig.DeleteScenarioFiles();
                // copy the new files
                RoboticsConfig.CopyScenarioFiles();
                // load the scenario
                scenario.ScenarioName = ("Survival of the Fattest");
                scenario.Load();
                // set the hosting environment to use it
                HostingEnvironment.EnvironmentScenario = scenario;

                _simEnv.TraceLogger.LogDebug(string.Format("Reloaded - Scenrio Version {0} vs Hosting Version {1}", scenario.HostingEnvironmentVersionInformation, HostingEnvironment.VersionInformation));
            }

            HostingEnvironment.ProcessingState = "Loading data from the GRID";

            // get a dal to read with if none set, mainly used for tests will load preset dals
            if (DAL == null)
            {
                // get a dal
                DAL = DalFactory.GetDalLoad(scenario.ScenarioName, HostingEnvironment);
            }

            // load the Hosting Environment
            HostingEnvironment = DAL.LoadHostingEnvironment(scenario.ScenarioName, HostingEnvironment, scenario.MaxLoadCount, SelectedOrganismsToLoad);

            _simEnv.TraceLogger.LogDebug(string.Format("Loaded scenario {0} from {1}", scenario.ScenarioName, DAL));

            // check and make sure we loaded some organisms
            if (HostingEnvironment == null)
            {
                _simEnv.TraceLogger.LogWarn("Environment not loaded");
            }

            // dump fitness funciton
            CreateScenarioFitnessAndRestrctions(scenario);

            // update the scenario organisms
            HostingEnvironment.Organisms.Alive = scenario.CompareEnvironmentUpdateRequirements(HostingEnvironment.Organisms, HostingEnvironment);

            // set all organisms times
            SetMinHeartBeatTimeForAllOrgs();

            // setup a timer if its set
            if (!Globals.BreakTime.Equals(new TimeSpan(0)))
                _timer = new System.Threading.Timer(RestartTimerCallback, null, Globals.BreakTime, new TimeSpan(-1));

            _simEnv.TraceLogger.LogDebug(string.Format("Break Time : {0}", Globals.BreakTime.ToString()));

            // dump the scenario overview
            _simEnv.TraceLogger.LogDebug(scenario.ToString());
            // start the hosting environment
            HostingEnvironment.Start();

            HostingEnvironment.ProcessingState = "Saving data to the GRID";
            DAL = DalFactory.GetDalSave();
            // save before shutdown
            if (HostingEnvironment.FitnessFunction != null)
                if (!DAL.SaveHostingEnvironment(scenario.ScenarioName, HostingEnvironment, scenario.MaxSaveCount))
                    _simEnv.TraceLogger.LogWarn("Environment not saved");

            // purge all orgs from memmory
            DisposeOldOrganisms();


            // upload the results, only upload on errors from now on...
#if DEBUG
            _simEnv.TraceLogger.UploadLogs("Results");
#endif
            // kill the timer
            if (_timer != null)
                _timer.Dispose();
        }

        private void DisposeOldOrganisms()
        {
            foreach (var organism in HostingEnvironment.Organisms)
            {
                organism.Dispose();
            }

            foreach (var organism in HostingEnvironment.Organisms.Inactive)
            {
                organism.Dispose();
            }
        }

        private void SetMinHeartBeatTimeForAllOrgs()
        {
            if (SimEnv.Instance.UsingHW)
            {
                // if using real hardware
                HostingEnvironment.HBOrganismCollectionMin = new TimeSpan(0, 0, 0, 0, 10);
            }
            else
            {
                // else give us a delay so we dont max out
                HostingEnvironment.HBOrganismCollectionMin = new TimeSpan(0, 0, 0, 0, 200);
            }
        }

        private void CreateScenarioFitnessAndRestrctions(IScenario scenario)
        {
            _simEnv.TraceLogger.LogDebug("Switching Fitness function : " + scenario.FitnessFunction);

            // create the fitness function
            switch (scenario.FitnessFunction)
            {
                case "CI.Mesopotamia.CollisionDetection":
                    HostingEnvironment.FitnessFunction = new CollisionDetection();
                    break;
                case "CI.Mesopotamia.MNTABHD":
                    HostingEnvironment.FitnessFunction = new MNTABHD();
                    break;
                case "CI.Mesopotamia.GenesisMNTABHD":
                    HostingEnvironment.FitnessFunction = new GenesisMNTABHD();
                    break;
                // used for testing
                case "CI.Misc.MockFitnessFunction":
                    HostingEnvironment.FitnessFunction = new MockFitnessFunction();
                    break;
                case Globals.NoFitnessFunction:
                    HostingEnvironment.FitnessFunction = null;
                    break;
                default:
                    throw new NotImplementedException();
            }

            // dump simulation restrictions
            _simEnv.TraceLogger.LogDebug("Scenario Resctrictions : " + scenario.SimulationRestrictions);

            // create the simulation restrictions
            switch (scenario.SimulationRestrictions)
            {
                case "CI.Mesopotamia.SurvivalFattest":
                    HostingEnvironment.Simulation = new SurvivalFattest();
                    break;
                // used for testing
                case "CI.Misc.MockSimulation":
                    HostingEnvironment.Simulation = new MockSimulation();
                    break;
                case "CI.Mesopotamia.SimpleSimulationRestrictions":
                    HostingEnvironment.Simulation = new SimpleSimulationRestrictions();
                    break;
                case "":
                    HostingEnvironment.Simulation = null;
                    break;
                default:
                    // todo : change this to restart with new scneario or something other than hard crash.
                    throw new NotImplementedException();
            }
        }

        /// <summary>
        /// break at specific time
        /// restart scenario
        /// </summary>
        /// <param name="value"></param>
        private void RestartTimerCallback(Object value)
        {
            const string restartMsg = "Timer restart, signaling to restart system";
            _simEnv.TraceLogger.LogDebug(restartMsg);
            _simEnv.TriggerScenarioRestart(restartMsg);
        }

    }
}